package it.unibo.refolding.pico;

import it.unibo.refolding.common.CommonThings;
import it.unibo.refolding.common.DoubleQuartet;
import it.unimi.dsi.fastutil.doubles.DoubleList;

import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;

public class RetractionCurve {

    public final String path;
    public final double springConstantNPerM;
    public final List<RetractionSegment> segments;

    public RetractionCurve(String path, double springConstantNPerM, List<RetractionSegment> segments) {
        this.path = path;
        this.springConstantNPerM = springConstantNPerM;
        this.segments = segments;
    }

    @Override
    public int hashCode() {
        return Objects.hash(path, springConstantNPerM, segments);
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof RetractionCurve)) {
            return false;
        }
        RetractionCurve other = (RetractionCurve) obj;
        return Objects.equals(path, other.path)
            & Double.doubleToLongBits(springConstantNPerM) == Double
                .doubleToLongBits(other.springConstantNPerM) & segments.equals(other.segments);
    }

    @Override
    public String toString() {
        return repr();
    }

    public String repr() {
        // For a canonical representation of the value, use
        // Float.toString(float) or Double.toString(double) as appropriate.
        return String.format(
            Locale.ROOT, "RetractionCurve(r'%s', %s, %s)", path,
            Double.toString(springConstantNPerM), RetractionSegment.repr(segments));
    }

    public static final String oldCurveRegex = String.format(
        "RetractionCurve\\(r'(%s)', (%s), (%s), (%s)\\)", "[^']*", CommonThings.FP_REGEX,
        CommonThings.FP_REGEX, "\\[[^]]*\\]");
    public static final Pattern oldCurvePattern = Pattern.compile(oldCurveRegex);

    public static final String curveRegex = String.format(
        "RetractionCurve\\(r'(%s)', (%s), (%s)\\)", "[^']*", CommonThings.FP_REGEX,
        "\\[.*\\]");
    public static final Pattern curvePattern = Pattern.compile(curveRegex);

    public static RetractionCurve parseRepr(String s) {
        Matcher oldMatcher = oldCurvePattern.matcher(s);
        boolean oldMatch = oldMatcher.matches();
        if (oldMatch) {
            return new RetractionCurve(
                oldMatcher.group(1), Double.parseDouble(oldMatcher.group(2)),
                ImmutableList.of(new RetractionSegment(
                    Double.parseDouble(oldMatcher.group(3)),
                    RetractionPeak.parseListRepr(oldMatcher.group(4)), 0,
                    0)));
        } else {
            Matcher matcher = curvePattern.matcher(s);
            boolean match = matcher.matches();
            Preconditions.checkArgument(match, "%s doesn't match RetractionCurve pattern.", s);
            return new RetractionCurve(
                matcher.group(1), Double.parseDouble(matcher.group(2)),
                RetractionSegment.parseListRepr(matcher.group(3)));
        }
    }

    public static RetractionCurve empty(String path) {
        return new RetractionCurve(path, 0, ImmutableList.<RetractionSegment>of());
    }

    public DoubleList peakContourLengthsFromContactPointOnwards(int iteration) {
        return segments.get(iteration).peakContourLengthsFromContactPointOnwards();
    }

    public List<DoubleQuartet> contourLengthNonNegativeRuptureForceExtensionAndDistanceOfPeaksFromContactPointOnwards(
      int iteration) {
        return segments.get(iteration)
            .contourLengthNonNegativeRuptureForceExtensionAndDistanceOfPeaksFromContactPointOnwards(springConstantNPerM);
    }

    public RetractionSegment segment(int iteration) {
        return segments.get(iteration);
    }

    public int size() {
        return segments.size();
    }
    
    public boolean isEmpty() {
        return segments.isEmpty();
    }
}
